Skip to main content

compio_driver\sys\driver\iocp/
mod.rs

1use std::{
2    collections::HashMap, marker::PhantomData, os::windows::io::AsRawHandle, sync::Arc,
3    time::Duration,
4};
5
6use flume::{Receiver, Sender};
7use windows_sys::Win32::{Foundation::ERROR_OPERATION_ABORTED, System::IO::OVERLAPPED};
8
9use crate::{
10    AsyncifyPool, DriverType, Entry, ErasedKey, ProactorBuilder,
11    control::Carrier,
12    sys::{driver::AwakeFlag, extra::IocpExtra, prelude::*},
13};
14
15mod cp;
16mod wait;
17
18mod_use![op];
19
20/// Operation type.
21pub enum OpType {
22    /// An overlapped operation.
23    Overlapped,
24    /// A blocking operation, needs a thread to spawn. The `operate` method
25    /// should be thread safe.
26    Blocking,
27    /// A Win32 event object to be waited. The user should ensure that the
28    /// handle is valid till operation completes. The `operate` method should be
29    /// thread safe.
30    Event(RawFd),
31}
32
33/// Low-level driver of IOCP.
34pub(crate) struct Driver {
35    notify: Arc<Notify>,
36    waits: HashMap<usize, wait::Wait>,
37    pool: AsyncifyPool,
38    completed_tx: Sender<Entry>,
39    completed_rx: Receiver<Entry>,
40    _local_marker: PhantomData<ErasedKey>,
41}
42
43impl Driver {
44    pub fn new(builder: &ProactorBuilder) -> io::Result<Self> {
45        instrument!(compio_log::Level::TRACE, "new", ?builder);
46
47        let port = cp::Port::new()?;
48        let driver = port.as_raw_handle() as _;
49        let overlapped = Overlapped::new(driver);
50        let notify = Arc::new(Notify::new(port, overlapped));
51        let (completed_tx, completed_rx) = flume::unbounded();
52
53        Ok(Self {
54            notify,
55            completed_tx,
56            completed_rx,
57            waits: HashMap::default(),
58            pool: builder.create_or_get_thread_pool(),
59            _local_marker: PhantomData,
60        })
61    }
62
63    pub fn driver_type(&self) -> DriverType {
64        DriverType::IOCP
65    }
66
67    fn port(&self) -> &cp::Port {
68        &self.notify.port
69    }
70
71    pub(in crate::sys) fn default_extra(&self) -> IocpExtra {
72        IocpExtra::new(self.port().as_raw_handle() as _)
73    }
74
75    pub fn attach(&mut self, fd: RawFd) -> io::Result<()> {
76        self.port().attach(fd)
77    }
78
79    pub fn cancel(&mut self, key: ErasedKey) {
80        instrument!(compio_log::Level::TRACE, "cancel", ?key);
81        trace!("cancel RawOp");
82        let optr = key.borrow().extra_mut().optr();
83        if let Some(w) = self.waits.get_mut(&key.as_raw())
84            && w.cancel().is_ok()
85        {
86            // The pack has been cancelled successfully, which means no packet
87            // will be post to IOCP. Need not set the result because
88            // `create_entry` handles it.
89            self.port().post_raw(optr).ok();
90        }
91        trace!("call OpCode::cancel");
92        // It's OK to fail to cancel.
93        key.borrow().carrier.cancel(optr.cast()).ok();
94    }
95
96    pub fn push(&mut self, key: ErasedKey) -> Poll<io::Result<usize>> {
97        instrument!(compio_log::Level::TRACE, "push", ?key);
98        trace!("push RawOp");
99        let mut op = key.borrow();
100        let optr = op.extra_mut().optr();
101        let op_type = op.carrier.op_type();
102        match op_type {
103            OpType::Overlapped => unsafe {
104                let res = op.carrier.operate(optr.cast());
105                drop(op);
106                if res.is_pending() {
107                    key.into_raw();
108                }
109                res
110            },
111            OpType::Blocking => {
112                drop(op);
113                self.push_blocking(key);
114                Poll::Pending
115            }
116            OpType::Event(e) => {
117                drop(op);
118                self.waits
119                    .insert(key.as_raw(), wait::Wait::new(self.notify.clone(), e, key)?);
120                Poll::Pending
121            }
122        }
123    }
124
125    fn push_blocking(&mut self, key: ErasedKey) {
126        let notify = self.notify.clone();
127        let tx = self.completed_tx.clone();
128
129        // SAFETY: we're submitting into the driver, so it's safe to freeze
130        // here.
131        let mut key = unsafe { key.freeze() };
132
133        let mut closure = move || {
134            let res = key.as_mut().operate_blocking();
135            let entry = Entry::new(key.into_inner(), res);
136            _ = tx.send(entry);
137            notify.wake();
138        };
139
140        while let Err(e) = self.pool.dispatch(closure) {
141            closure = e.0;
142            std::thread::yield_now();
143        }
144    }
145
146    pub fn flush(&mut self) -> bool {
147        self.notify.reset()
148    }
149
150    fn create_entry(
151        notify: *const Overlapped,
152        waits: &mut HashMap<usize, wait::Wait>,
153        entry: cp::RawEntry,
154    ) -> Option<Entry> {
155        if entry.overlapped.cast_const() == notify {
156            return None;
157        }
158
159        let entry = Entry::new(
160            unsafe { ErasedKey::from_optr(entry.overlapped) },
161            entry.result,
162        );
163
164        // if there's no wait, just return the entry
165        let Some(w) = waits.remove(&entry.user_data()) else {
166            return Some(entry);
167        };
168
169        let entry = if w.is_cancelled() {
170            Entry::new(
171                entry.into_key(),
172                Err(io::Error::from_raw_os_error(ERROR_OPERATION_ABORTED as _)),
173            )
174        } else if entry.result.is_err() {
175            entry
176        } else {
177            let key = entry.into_key();
178            let result = key.borrow().operate_blocking();
179            Entry::new(key, result)
180        };
181
182        Some(entry)
183    }
184
185    pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<()> {
186        instrument!(compio_log::Level::TRACE, "poll", ?timeout);
187
188        let notify = &self.notify.overlapped as *const Overlapped;
189
190        let mut has_entry = false;
191        while let Ok(entry) = self.completed_rx.try_recv() {
192            entry.notify();
193            has_entry = true;
194        }
195        if self.notify.reset() {
196            has_entry = true;
197        }
198
199        if !has_entry {
200            for e in self.notify.port.poll(timeout)? {
201                if let Some(e) = Self::create_entry(notify, &mut self.waits, e) {
202                    self.notify.set_awake();
203                    e.notify()
204                }
205            }
206        }
207        self.notify.set_awake();
208
209        Ok(())
210    }
211
212    pub fn waker(&self) -> Waker {
213        Waker::from(self.notify.clone())
214    }
215
216    pub fn pop_multishot(&mut self, _: &ErasedKey) -> Option<BufResult<usize, crate::sys::Extra>> {
217        None
218    }
219}
220
221impl AsRawFd for Driver {
222    fn as_raw_fd(&self) -> RawFd {
223        self.port().as_raw_handle() as _
224    }
225}
226
227/// A notify handle to the inner driver.
228pub(crate) struct Notify {
229    port: cp::Port,
230    overlapped: Overlapped,
231    awake: AwakeFlag,
232}
233
234impl Notify {
235    fn new(port: cp::Port, overlapped: Overlapped) -> Self {
236        Self {
237            port,
238            overlapped,
239            awake: AwakeFlag::new(),
240        }
241    }
242
243    fn set_awake(&self) {
244        self.awake.set();
245    }
246
247    fn reset(&self) -> bool {
248        self.awake.reset()
249    }
250}
251
252impl Wake for Notify {
253    fn wake(self: Arc<Self>) {
254        self.wake_by_ref();
255    }
256
257    fn wake_by_ref(self: &Arc<Self>) {
258        if !self.awake.wake() {
259            self.port.post_raw(&self.overlapped).ok();
260        }
261    }
262}